use shared::{clone, NetemProfile, ServerInfo};
use yew::suspense::use_future;
use std::cell::RefCell;
use std::ops::Deref;
use std::rc::Rc;
use wasm_bindgen_futures::spawn_local;
use yew::prelude::*;
use yew_hooks::prelude::*;

use crate::api;
use crate::components::Dropdown;
use crate::edit_profile::{EditProfile, EditProfileCallbacks};
use crate::launch_button::{LaunchButton, LaunchButtonState};
use crate::preset_button::PresetButton;

#[derive(Debug, PartialEq)]
pub enum ProfileState {
    Init,
    Running(NetemProfile),
    Stopped(NetemProfile),
    Starting(NetemProfile),
    Stopping(NetemProfile),
}

pub enum ProfileAction {
    Start(UseReducerHandle<ProfileState>, NetemProfile),
    Stop(UseReducerHandle<ProfileState>, NetemProfile),
    RemoteUpdate(Result<NetemProfile, String>),
    LocalUpdate(NetemProfile),
    Started,
    Stopped,
}

impl Reducible for ProfileState {
    type Action = ProfileAction;

    fn reduce(self: std::rc::Rc<Self>, action: Self::Action) -> std::rc::Rc<Self> {
        match self.deref() {
            ProfileState::Init => {
                if let ProfileAction::RemoteUpdate(p) = action {
                    if let Ok(p) = p {
                        if p.active {
                            ProfileState::Running(p).into()
                        } else {
                            ProfileState::Stopped(p).into()
                        }
                    } else {
                        ProfileState::Init.into()
                    }
                } else {
                    self
                }
            }
            ProfileState::Running(p) => match action {
                ProfileAction::RemoteUpdate(Err(_)) => ProfileState::Init.into(),
                ProfileAction::Stop(state, p) => {
                    let profile = NetemProfile {
                        active: false,
                        ..p.clone()
                    };
                    let state = state.clone();
                    spawn_local(async move {
                        let res = api::update_profile(profile).await;
                        match res {
                            Ok(_profile) => {
                                state.dispatch(ProfileAction::Stopped);
                            }
                            Err(err) => {
                                log::error!("stop profile error {}", err);
                            }
                        }
                    });
                    ProfileState::Stopping(p).into()
                }
                ProfileAction::Stopped => ProfileState::Stopped(p.clone()).into(),
                _ => self,
            },
            ProfileState::Stopped(p) => match action {
                ProfileAction::RemoteUpdate(Err(_)) => ProfileState::Init.into(),
                ProfileAction::LocalUpdate(p) => ProfileState::Stopped(p).into(),
                ProfileAction::Start(state, p) => {
                    let profile = NetemProfile {
                        active: true,
                        ..p.clone()
                    };
                    let state = state.clone();
                    spawn_local(async move {
                        let res = api::update_profile(profile).await;
                        match res {
                            Ok(_profile) => {
                                state.dispatch(ProfileAction::Started);
                            }
                            Err(err) => {
                                log::error!("start profile error {}", err);
                            }
                        }
                    });
                    ProfileState::Starting(p).into()
                }
                ProfileAction::Started => ProfileState::Running(p.clone()).into(),
                _ => self,
            },
            ProfileState::Starting(p) => match action {
                ProfileAction::RemoteUpdate(Err(_)) => ProfileState::Init.into(),
                ProfileAction::Started => ProfileState::Running(p.clone()).into(),
                ProfileAction::Stopped => ProfileState::Stopped(p.clone()).into(),
                _ => self,
            },
            ProfileState::Stopping(p) => match action {
                ProfileAction::RemoteUpdate(Err(_)) => ProfileState::Init.into(),
                ProfileAction::Stopped => ProfileState::Stopped(p.clone()).into(),
                ProfileAction::Started => ProfileState::Running(p.clone()).into(),
                _ => self,
            },
        }
    }
}

#[function_component]
pub fn HomePage() -> Html {
    let server_info_state = use_state_eq(|| ServerInfo::default());
    let profile_state = use_reducer_eq(|| ProfileState::Init);
    let callbacks = use_mut_ref(|| Option::<EditProfileCallbacks>::None);

    let get_server_info = clone!([server_info_state], move |_: &()| {
        spawn_local(clone!([server_info_state], async move {
            loop {
                let res = crate::api::get_server_info().await;
                if let Ok(server_info) = res {
                    server_info_state.set(server_info);
                    break;
                }
                gloo::timers::future::TimeoutFuture::new(5000).await;
            }
        }));
    });
    let get_profile = clone!([profile_state], move |_: &()| {
        spawn_local(clone!([profile_state], async move {
            loop {
                let res = crate::api::get_profile(uuid::Uuid::default()).await;
                profile_state.dispatch(ProfileAction::RemoteUpdate(res));
                gloo::timers::future::TimeoutFuture::new(5000).await;
            }
        }));
    });
    let _loader1 = use_memo(get_server_info, ());
    let _loader2 = use_memo(get_profile, ());

    let mut classes = classes!("home");
    if let ProfileState::Running(_) = profile_state.deref() {
        classes.push("running");
    }
    html! {
        <div class={classes}>
            <div class="upper-box">
                <div>
                    {make_launch_row(profile_state.clone(), callbacks.clone())}
                    {make_comment_widget(profile_state.deref())}
                </div>
                <div class="text-align-right">
                    <div class="title">{ server_info_state.format_title() }</div>
                    <div class="subtitle">{ server_info_state.format_detail() }</div>
                </div>
            </div>
            <div class="spacer">
            </div>
            <div>
                {make_content_widget(profile_state.clone(), callbacks.clone())}
            </div>
        </div>
    }
}

fn make_launch_row(
    state: UseReducerHandle<ProfileState>,
    callbacks: Rc<RefCell<Option<EditProfileCallbacks>>>,
) -> Html {
    let preset = if let ProfileState::Stopped(_) = state.deref() {
        make_preset_dropdown(state.clone())
    } else {
        html! { <></> }
    };
    html! {
        <div class="launch-row">
            {make_launch_button(state, callbacks)}
            {preset}
        </div>
    }
}

fn make_launch_button(
    state: UseReducerHandle<ProfileState>,
    callbacks: Rc<RefCell<Option<EditProfileCallbacks>>>,
) -> Html {
    let button_state = match state.deref() {
        ProfileState::Init => LaunchButtonState::Loading,
        ProfileState::Stopped(_) => LaunchButtonState::Stopped,
        ProfileState::Running(_) => LaunchButtonState::Running,
        ProfileState::Starting(_) | ProfileState::Stopping(_) => LaunchButtonState::Loading,
    };
    let onclick = Callback::from(clone!([state], move |_| {
        match state.deref() {
            ProfileState::Stopped(_) => {
                if let Some(ref cb) = callbacks.borrow().deref() {
                    let profile = cb.get_editing.emit(());
                    state.dispatch(ProfileAction::Start(state.clone(), profile));
                }
            }
            ProfileState::Running(p) => {
                state.dispatch(ProfileAction::Stop(state.clone(), p.clone()));
            }
            ProfileState::Init => {
                let _ = web_sys::window().map(|w| {
                    let _ = w.location().reload();
                });
            }
            ProfileState::Starting(_) | ProfileState::Stopping(_) => (),
        };
    }));
    html! {
        <LaunchButton state={button_state} {onclick} />
    }
}

fn make_preset_dropdown(state: UseReducerHandle<ProfileState>) -> Html {
    let onclick = Callback::from(clone!([state], move |profile: NetemProfile| {
        state.dispatch(ProfileAction::LocalUpdate(profile));
    }));
    let preset_buttons = shared::NetemProfile::presets()
        .into_iter()
        .map(|profile| {
            html! {
                <PresetButton {profile} onclick={onclick.clone()} />
            }
        })
        .collect::<Vec<_>>();
    html! {
        <Dropdown>
            {preset_buttons}
        </Dropdown>
    }
}

#[function_component]
fn RunningStateComment() -> Html {
    let timer = use_state(|| 0usize);
    let _ = use_interval(
        clone!([timer], move || {
            timer.set((*timer).wrapping_add(1));
        }),
        500,
    );
    let dots = "~".repeat(1 + (*timer % 3));
    let text = format!("服务器运行中{}", dots);
    html! {
    <>
        <p class="comment">{text}</p>
        <p class="comment">{"要修改网络参数，请先停止服务器。"}</p>
    </>
    }
}

fn make_comment_widget(state: &ProfileState) -> Html {
    let texts: &[&str] = match state {
        ProfileState::Init => &["正在获取配置......"],
        ProfileState::Running(_) => {
            return html! {
                <RunningStateComment />
            };
        },
        ProfileState::Stopped(_) => &["请配置好网络参数后，启动服务器。"],
        ProfileState::Starting(_) => &["正在启动服务器......"],
        ProfileState::Stopping(_) => &["正在停止服务器......"],
    };
    let texts = texts.into_iter().map(|text| {
        html! {
            <p class="comment">{text}</p>
        }
    });
    html! {
    <>
        {for texts}
    </>
    }
}

fn make_content_widget(
    state: UseReducerHandle<ProfileState>,
    callbacks: Rc<RefCell<Option<EditProfileCallbacks>>>,
) -> Html {
    // log::info!("make_content_widget state={:?}", state.deref());
    match state.deref() {
        ProfileState::Init => make_editing_widget(true, &NetemProfile::default(), callbacks),
        ProfileState::Stopped(p) => make_editing_widget(false, p, callbacks),
        ProfileState::Starting(p) | ProfileState::Running(p) | ProfileState::Stopping(p) => {
            make_editing_widget(true, p, callbacks)
        }
    }
}

fn make_editing_widget(
    disabled: bool,
    p: &NetemProfile,
    callbacks: Rc<RefCell<Option<EditProfileCallbacks>>>,
) -> Html {
    // 当从服务器获取到新的Profile时，更新key，丢弃编辑中的值
    let key = format!(
        "{}:D{}L{}:{}J{}B{}/{}",
        p.name,
        p.delay(),
        p.loss(),
        p.is_poisson_loss(),
        p.jitter(),
        p.burst().0,
        p.burst().1
    );
    html! {
        <EditProfile {key} {disabled} profile={p.clone()} {callbacks} />
    }
}
